package self.zhangkang.chart.view.kline

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import self.zhangkang.chart.data.KLineInfo
import self.zhangkang.chart.tools.CanvasTools
import self.zhangkang.chart.tools.DateTools
import self.zhangkang.chart.tools.dp2px
import self.zhangkang.chart.view.IndexNameConfig
import self.zhangkang.chart.view.OnLastValueChangeListener
import self.zhangkang.chart.view.OnTabNameClickListener
import self.zhangkang.chart.view.pool.PathPool
import self.zhangkang.chart.view.pool.RectPool
import self.zhangkang.chart.view.touch.TouchHelper
import kotlin.math.floor

/**
 * @author : Android-张康
 * created on: 2023/5/4 10:59
 * description: CandlestickChartView K线图
 */
class KLineView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), TouchHelper.OnListener {

    companion object {

        /**
         * 默认展示K线数量
         */
        private const val DEFAULT_K_LINE_NUMBER = 300

        /**
         * 单根K线默认宽度（打开时）：5 单位 dp
         */
        private const val K_LINE_DEFAULT_WIDTH = 5

        /**
         * 单根K线最大宽度：20 单位 dp
         */
        private const val CANDLESTICK_MAX_WIDTH = 20

        /**
         * 单根K线最小宽度：1 单位 dp
         */
        private const val CANDLESTICK_MIN_WIDTH = 1

        /**
         * K线之间的间距：2 单位 dp
         */
        private const val CANDLESTICK_SPACE = 2

        /**
         * 加载更多触发的数量
         */
        private const val LOAD_MORE_SIZE = 30
    }

    private val mKLineData = mutableListOf<KLineInfo>()

    /**
     * 蜡烛默认展示的数量
     */
    private var mShowCandlestickNumber = DEFAULT_K_LINE_NUMBER

    /**
     * 蜡烛的最大宽度
     */
    private val candlestickMaxWidth = CANDLESTICK_MAX_WIDTH.dp2px(context)

    /**
     * 蜡烛的最小计算宽度
     */
    private val candlestickCalculationMinWidth = CANDLESTICK_MIN_WIDTH.dp2px(context)

    /**
     * 蜡烛的最小宽度
     */
    private val candlestickMinWidth = candlestickCalculationMinWidth / 4 * 3

    /**
     * 单个K线绘制宽度
     */
    private var mCandlestickWidth = K_LINE_DEFAULT_WIDTH.dp2px(context)

    /**
     * 蜡烛之间默认的距离
     */
    private val candlestickDefaultSpace = CANDLESTICK_SPACE.dp2px(context)

    /**
     * K线之间的距离
     */
    private var mCandlestickSpace = candlestickDefaultSpace

    /**
     * 阳线是否实心
     */
    private var mYangLineIsFill = true

    private var mIsGetWidth = false

    private var mStartDataIndex = 0

    private var mEndDataIndex = 0

    private val mKLineViewColor = KLineViewColor(context)

    private val mKLineViewDimen = KLineViewDimen(context)

    /**
     * 线的画笔
     */
    private val mLinePaint: Paint = Paint().apply {
        style = Paint.Style.STROKE
        isAntiAlias = true
        isDither = true
    }

    /**
     * 文字的画笔
     */
    private val mTextPaint = Paint().apply {
        isAntiAlias = true
        isDither = true
    }

    /**
     * K线是否为一条线
     */
    private var mKLineIsLine = false

    /**
     * 缓存Path
     */
    private val mPathPool = PathPool()

    /**
     * 缓存 Rect
     */
    private val mRectPool = RectPool()

    /**
     * 触摸协助类
     */
    private val mTouchHelper: TouchHelper = TouchHelper(context, this, this)

    private val indexNameConfig = IndexNameConfig(context)

    private val mIndexInfoListHelper = IndexInfoListHelper(
        pathPool = mPathPool,
        rectPool = mRectPool,
        kLineViewColor = mKLineViewColor,
        kLineViewDimen = mKLineViewDimen,
        indexNameConfig = indexNameConfig
    )

    private val mKLineVolumeList = mutableListOf(
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.KLine),
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.Volume),
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.MACD),
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.KDJ)
    )

    /**
     * 副图的高度
     */
    private var mSubplotHeight = mKLineViewDimen.subplotHeight

    /**
     * 记录当前触摸的数据下标
     */
    private var mCurrentDataIndex = -1

    /**
     * 记录当前触摸的 X 坐标
     */
    private var mLongPressX = -1F

    /**
     * 记录当前触摸的 Y 坐标
     */
    private var mLongPressY = -1F

    /**
     * 记录最后触摸的指标
     */
    private var mLastTouchIndex = -1

    private var mOnValueSelectedListener: OnLastValueChangeListener? = null

    private var mOnTabNameClickListener: OnTabNameClickListener<KLineIndex>? = null

    private var mLoadMoreEnabled = true

    private var mOnKLineViewLoadMoreListener: OnKLineViewLoadMoreListener? = null

    init {
        post {
            calculationDrawNumber()
            calculationMainAndSubplotHeight()
        }
    }

    override fun computeScroll() {
        super.computeScroll()
        mTouchHelper.computeScrollOffset()
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        return mTouchHelper.onTouchEvent(event)
    }

    override fun onDraw(canvas: Canvas) {
        if (!mIsGetWidth) {
            return
        }
        val paddingStart = paddingStart.toFloat()
        val paddingEnd = paddingEnd.toFloat()
        val paddingTop = paddingTop.toFloat()
        val paddingBottom = paddingBottom.toFloat()
        val startX = paddingStart
        val stopX = width - paddingEnd
        val startY = paddingTop
        val stopY = height - paddingBottom
        val space = mKLineViewDimen.subplotSpace

        //绘制指标图
        val subplotHeight = mSubplotHeight
        var subplotStartY = startY
        var subplotStopY: Float
        val lineHeight = mKLineViewDimen.borderLineStrokeWidth
        var isFirst = true
        mKLineVolumeList.forEach {
            val canvasStartY: Float
            if (isFirst) {
                canvasStartY = subplotStartY + mKLineViewDimen.mainTopSpace
                subplotStopY = canvasStartY + it.getProportion() * subplotHeight
            } else {
                canvasStartY = subplotStartY + mKLineViewDimen.subplotSpace
                subplotStopY = canvasStartY + it.getProportion() * subplotHeight
            }
            //先绘制背景
            it.drawBackground(
                canvas = canvas,
                startX = startX,
                startY = canvasStartY,
                stopX = stopX,
                stopY = subplotStopY,
                textPaint = mTextPaint,
                linePaint = mLinePaint.apply {
                    strokeWidth = mKLineViewDimen.borderLineStrokeWidth
                    color = mKLineViewColor.borderLineColor
                }
            )
            subplotStartY = if (isFirst) {
                subplotStopY + mKLineViewDimen.dateSpace
            } else {
                subplotStopY
            }
            isFirst = false
        }
        if (mKLineData.isEmpty()) {
            return
        }
        val dateStartY =
            startY + mKLineViewDimen.mainTopSpace + mKLineVolumeList[0].getProportion() * subplotHeight
        val dateStopY = dateStartY + mKLineViewDimen.dateSpace
        //绘制日期
        drawDate(canvas, startX, dateStartY, stopX, dateStopY)
        //绘制指标信息
        val currentKLineInfo: KLineInfo = getCurrentData()
        subplotStartY = startY
        subplotStopY = subplotStartY
        isFirst = true
        mKLineVolumeList.forEach {
            val canvasStartY: Float
            if (isFirst) {
                canvasStartY = subplotStartY
                subplotStopY = subplotStartY + mKLineViewDimen.mainTopSpace
            } else {
                canvasStartY = subplotStartY
                subplotStopY = subplotStartY + mKLineViewDimen.subplotSpace
            }
            //绘制当前指标的值
            it.drawCurrentIndexInfo(
                canvas = canvas,
                startX = startX,
                startY = canvasStartY + mKLineViewDimen.borderLineStrokeWidth,
                stopX = stopX,
                stopY = subplotStopY - mKLineViewDimen.borderLineStrokeWidth,
                marginTop = lineHeight,
                span = 3,
                indexValueHorizontalSpace = mKLineViewDimen.valueHorizontalSpace,
                indexValueVerticalSpace = mKLineViewDimen.valueVerticalSpace,
                kLineInfo = currentKLineInfo,
                textPaint = mTextPaint,
                linePaint = mLinePaint,
                kLineIsLine = mKLineIsLine
            )
            subplotStartY = if (isFirst) {
                subplotStopY + it.getProportion() * subplotHeight + mKLineViewDimen.dateSpace
            } else {
                subplotStopY + it.getProportion() * subplotHeight
            }
            isFirst = false
        }
        subplotStartY = startY
        subplotStopY = subplotStartY
        isFirst = true
        mKLineVolumeList.forEach {
            val canvasStartY: Float
            if (isFirst) {
                canvasStartY = subplotStartY + mKLineViewDimen.mainTopSpace
                subplotStopY = canvasStartY + it.getProportion() * subplotHeight
            } else {
                canvasStartY = subplotStartY + mKLineViewDimen.subplotSpace
                subplotStopY = canvasStartY + it.getProportion() * subplotHeight
            }
            it.beforeDrawItemContent(
                canvas = canvas,
                startX = startX,
                startY = canvasStartY,
                stopX = stopX,
                stopY = subplotStopY,
                paint = mLinePaint,
                candlestickWidth = mCandlestickWidth,
                candlestickWidthSpace = mCandlestickSpace.toFloat(),
                kLineInfo = currentKLineInfo,
                kLineIsLine = mKLineIsLine
            )
            subplotStartY = subplotStopY
            if (isFirst) {
                subplotStartY = subplotStopY + mKLineViewDimen.dateSpace
            }
            isFirst = false
        }
        //蜡烛图的宽度
        val candlestickWidth: Float = if (mKLineIsLine) {
            candlestickCalculationMinWidth
        } else {
            mCandlestickWidth
        }
        val startIndex = mStartDataIndex
        val endIndex = mEndDataIndex

        val drawWidth = stopX - startX
        val showKLineNumber = mShowCandlestickNumber
        val dataNumber = endIndex - startIndex + 1
        val drawNumber = Math.min(dataNumber, showKLineNumber)
        //drawNumber == showKLineNumber 说明当前可以绘制满整个屏幕，第一次位移到最右方即可。
        //否则就说明当前蜡烛的数量不足以绘制满整个屏幕，位移（蜡烛图宽度+空隙）*(绘制数量-1)+边框线的宽度
        var totalTranslateX = if (drawNumber == showKLineNumber) {
            drawWidth - mKLineViewDimen.borderLineStrokeWidth - candlestickWidth
        } else {
            (candlestickWidth + mCandlestickSpace) * (drawNumber - 1) + mKLineViewDimen.borderLineStrokeWidth
        }
        val halfCandlestickWidth = candlestickWidth / 2
        val translateX = -mCandlestickSpace - candlestickWidth
        canvas.save()
        canvas.translate(totalTranslateX, 0F)
        var isFirstData = true
        for (index in endIndex downTo startIndex) {
            val kLineInfo = mKLineData[index]
            subplotStartY = startY
            subplotStopY = subplotStartY
            isFirst = true
            mKLineVolumeList.forEach {
                val canvasStartY: Float
                if (isFirst) {
                    canvasStartY = subplotStartY + mKLineViewDimen.mainTopSpace
                    subplotStopY = canvasStartY + it.getProportion() * subplotHeight
                } else {
                    canvasStartY = subplotStartY + mKLineViewDimen.subplotSpace
                    subplotStopY = canvasStartY + it.getProportion() * subplotHeight
                }
                it.drawItemContent(
                    canvas = canvas,
                    startX = startX,
                    startY = canvasStartY,
                    stopX = stopX,
                    stopY = subplotStopY,
                    textPaint = mTextPaint,
                    linePaint = mLinePaint,
                    kLineInfo = kLineInfo,
                    isFirst = isFirstData,
                    yangLineIsFill = mYangLineIsFill,
                    totalTranslateX = totalTranslateX,
                    candlestickWidth = candlestickWidth,
                    halfCandlestickWidth = halfCandlestickWidth,
                    kLineIsLine = mKLineIsLine
                )
                subplotStartY = subplotStopY
                if (isFirst) {
                    subplotStartY = subplotStopY + mKLineViewDimen.dateSpace
                }
                isFirst = false
            }
            canvas.translate(translateX, 0F)
            totalTranslateX += translateX
            isFirstData = false
        }
        canvas.restore()
        mKLineVolumeList.forEach {
            it.afterDrawItemContent(canvas, mLinePaint.apply {
                style = Paint.Style.STROKE
                strokeWidth = mKLineViewDimen.borderLineStrokeWidth
            })
        }
        if (mLongPressX in startX..stopX && mLongPressY in startY..stopY) {
            mLinePaint.color = mKLineViewColor.highlightLineColor
            mLinePaint.strokeWidth = mKLineViewDimen.highlightLineWidth
            canvas.drawLine(startX, mLongPressY, stopX, mLongPressY, mLinePaint)
            canvas.drawLine(
                mLongPressX,
                startY + mKLineViewDimen.mainTopSpace,
                mLongPressX,
                stopY,
                mLinePaint
            )
            findAvgIndexInfo()?.let {
                val leftText = it.first.getCurrentLeftValue(it.second)
                if (!leftText.isNullOrEmpty()) {
                    CanvasTools.drawHighlightLeftOrRightValue(
                        canvas = canvas,
                        startX = startX,
                        stopX = stopX,
                        startY = startY,
                        stopY = stopY,
                        longPressX = mLongPressX,
                        longPressY = mLongPressY,
                        textStartY = startY + mKLineViewDimen.mainTopSpace + subplotHeight * mKLineVolumeList.first()
                            .getProportion() + mKLineViewDimen.maxOrMinPriceTextPaddingLine,
                        rectPool = mRectPool,
                        drawText = leftText,
                        textPaint = mTextPaint,
                        backgroundColor = mKLineViewColor.highlightLineBgColor,
                        textSize = mKLineViewDimen.ordinateTextSize,
                        textColor = mKLineViewColor.highlightLineTextColor,
                        isLeft = true
                    )
                }
                val rightText = it.first.getCurrentRightValue(it.second)
                if (!rightText.isNullOrEmpty()) {
                    CanvasTools.drawHighlightLeftOrRightValue(
                        canvas = canvas,
                        startX = startX,
                        stopX = stopX,
                        startY = startY,
                        stopY = stopY,
                        longPressX = mLongPressX,
                        longPressY = mLongPressY,
                        textStartY = startY + mKLineViewDimen.mainTopSpace + subplotHeight * mKLineVolumeList.first()
                            .getProportion(),
                        rectPool = mRectPool,
                        drawText = rightText,
                        textPaint = mTextPaint,
                        backgroundColor = mKLineViewColor.highlightLineBgColor,
                        textSize = mKLineViewDimen.ordinateTextSize,
                        textColor = mKLineViewColor.highlightLineTextColor,
                        isLeft = false
                    )
                }
            }

            val bottomText = DateTools.toyyyy_MM_dd(currentKLineInfo.getDataTime())
            CanvasTools.drawHighlightBottomValue(
                canvas = canvas,
                startX = startX,
                stopX = stopX,
                startY = startY,
                stopY = stopY,
                longPressX = mLongPressX,
                textStartY = startY + mKLineViewDimen.mainTopSpace + subplotHeight * mKLineVolumeList.first()
                    .getProportion(),
                rectPool = mRectPool,
                drawText = bottomText,
                textPaint = mTextPaint,
                backgroundColor = mKLineViewColor.highlightLineBgColor,
                textSize = mKLineViewDimen.ordinateTextSize,
                textColor = mKLineViewColor.highlightLineTextColor,
                textPaddingHorizontal = mKLineViewDimen.textPaddingLine
            )
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        finishLongPress()
        calculationDrawNumber()
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mTouchHelper.onDetachedFromWindow()
    }

    fun setNewData(klineData: List<KLineInfo>) {
        setKLineData(klineData)
    }

    fun setLoadMoreData(newDataSize: Int, klineData: List<KLineInfo>) {
        val oldStartDataIndex = mStartDataIndex
        val oldEndDataIndex = mEndDataIndex
        val newStartDataIndex = oldStartDataIndex + newDataSize
        val newEndDataIndex = oldEndDataIndex + newDataSize
        mStartDataIndex = newStartDataIndex
        mEndDataIndex = newEndDataIndex
        setKLineData(klineData)
    }

    fun setKLineData(klineData: List<KLineInfo>) {
        resetData()
        val oldDataSize = mKLineData.size
        val newDataSize = klineData.size
        //如果新的数据长度小于旧的数据长度，关闭长按模式
        if (newDataSize < oldDataSize) {
            finishLongPress()
        }
        mKLineData.clear()
        mKLineData.addAll(klineData)
        calculation()
        postInvalidate()
    }

    fun updateData() {
        calculation()
        postInvalidate()
    }

    /**
     * 设置值选择监听
     *
     * @param onValueSelectedListener 值选择监听
     */
    fun setOnValueSelectedListener(onValueSelectedListener: OnLastValueChangeListener) {
        mOnValueSelectedListener = onValueSelectedListener
    }

    fun setConfig(config: List<KLineIndex>) {
        mKLineVolumeList.clear()
        mKLineVolumeList.addAll(config.mapNotNull {
            if (it == KLineIndex.UNKNOWN) {
                null
            } else {
                mIndexInfoListHelper.createKLineIndexInfo(it)
            }
        })
        calculation()
        invalidate()
    }

    fun updateConfig(position: Int, kLineIndex: KLineIndex) {
        if (kLineIndex == KLineIndex.UNKNOWN) {
            mKLineVolumeList.removeAt(position)
        } else {
            if (position >= mKLineVolumeList.size) {
                mKLineVolumeList.add(mIndexInfoListHelper.createKLineIndexInfo(kLineIndex))
            } else if (position >= 0) {
                mKLineVolumeList[position] = mIndexInfoListHelper.createKLineIndexInfo(kLineIndex)
            } else {
                return
            }
        }
        calculation()
        invalidate()
    }

    fun setOnTabNameClickListener(onTabNameClickListener: OnTabNameClickListener<KLineIndex>) {
        mOnTabNameClickListener = onTabNameClickListener
    }

    fun setLoadMoreEnabled(enabled: Boolean) {
        mLoadMoreEnabled = enabled
    }

    fun setOnLoadMoreListener(onKLineViewLoadMoreListener: OnKLineViewLoadMoreListener) {
        mOnKLineViewLoadMoreListener = onKLineViewLoadMoreListener
    }

    /**
     * 关闭长按
     */
    fun closeLongPress() {
        finishLongPress()
    }

    /**
     * 计算数据
     */
    private fun calculation() {
        calculationMainAndSubplotHeight()
        //计算绘制范围
        calculationDrawRangeByScale()
        //计算最大值和最小值
        calculationMaxAndMin()
    }

    /**
     * 缩放时，保持结束坐标不变，仅修改开始坐标
     */
    private fun calculationDrawRangeByScale() {
        val oldEndIndex = mEndDataIndex
        val showCandlestickNumber = mShowCandlestickNumber
        if (mKLineData.isEmpty()) {
            mStartDataIndex = 0
            mEndDataIndex = 0
            return
        }
        //计算绘制范围
        var startIndex: Int
        var endIndex: Int
        if (mKLineData.size > showCandlestickNumber) {
            endIndex = if (oldEndIndex == 0) {
                mKLineData.size - 1
            } else {
                oldEndIndex
            }
            startIndex = endIndex - showCandlestickNumber
            if (startIndex < 0) {
                startIndex = 0
                endIndex = showCandlestickNumber
            }
        } else {
            startIndex = 0
            endIndex = mKLineData.size - 1
        }
        mStartDataIndex = startIndex
        mEndDataIndex = endIndex
    }

    /**
     * 计算主副图的高度
     */
    private fun calculationMainAndSubplotHeight() {
        val drawHeight = height - paddingTop - paddingBottom
        var sum = 0
        mKLineVolumeList.forEach {
            sum += it.getProportion()
        }
        //计算的高度 = 绘制的高度 - 与副图之间的高度 * 副图数量 - 顶部的高度 - 时间的高度
        var calculationHeight =
            drawHeight - mKLineViewDimen.subplotSpace * (mKLineVolumeList.size - 1) * 1.0F - mKLineViewDimen.mainTopSpace - mKLineViewDimen.dateSpace
        calculationHeight /= sum
        val subplotHeight = calculationHeight
        mSubplotHeight = subplotHeight
    }

    /**
     * 计算最大值和最小值
     */
    private fun calculationMaxAndMin() {
        if (mKLineData.isEmpty()) {
            return
        }
        val startIndex: Int = mStartDataIndex
        val endIndex: Int = mEndDataIndex
        val startKLine = mKLineData[startIndex]
        mKLineVolumeList.forEach {
            it.calculationMaxAndMinBefore(startKLine)
        }
        for (index in startIndex..endIndex) {
            mKLineVolumeList.forEach {
                it.calculationMaxAndMin(mKLineData[index])
            }
        }
        mKLineVolumeList.forEach {
            it.calculationMaxAndMinAfter()
        }
    }

    /**
     * 计算绘制的数量
     *
     * 根据当前屏幕的宽度和蜡烛图的宽度计算
     *
     */
    private fun calculationDrawNumber() {
        val drawWidth =
            width - paddingStart - paddingEnd - 2 * mKLineViewDimen.borderLineStrokeWidth
        val candlestickSpace = candlestickDefaultSpace
        val candlestickWidth = if (mKLineIsLine) {
            candlestickCalculationMinWidth
        } else {
            mCandlestickWidth
        }
        val showCandlestickNumber =
            floor((drawWidth / (candlestickWidth + candlestickSpace)).toDouble()).toInt()
        //重新计算每个蜡烛图之间的间隔
        mCandlestickSpace =
            (drawWidth - (candlestickWidth * showCandlestickNumber)) / (showCandlestickNumber - 1)
        mShowCandlestickNumber = showCandlestickNumber
        mIsGetWidth = true
        invalidate()
    }

    override fun onScale(scaleFactor: Float) {
        mCandlestickWidth *= scaleFactor
        mKLineIsLine = false
        if (mCandlestickWidth > candlestickMaxWidth) {
            mCandlestickWidth = candlestickMaxWidth
        } else if (mCandlestickWidth < candlestickCalculationMinWidth) {
            if (mCandlestickWidth < candlestickMinWidth) {
                mCandlestickWidth = candlestickMinWidth
            }
            mKLineIsLine = true
        }
        //计算绘制的数量
        calculationDrawNumber()
        //计算绘制范围
        calculationDrawRangeByScale()
        //计算最大值和最小值
        calculationMaxAndMin()
        invalidate()
    }

    /**
     * 滚动页面
     *
     * @param scrollX 本次滚动的X
     */
    override fun onScroll(scrollX: Float) {
        var startIndex = mStartDataIndex
        val maxEnd = mKLineData.size - 1
        //增加灵敏度
        val scrollNumber = (if (scrollX >= 0) {
            scrollX / mCandlestickWidth + 0.75F
        } else {
            scrollX / mCandlestickWidth - 0.75F
        }).toInt()
        startIndex += scrollNumber
        if (startIndex < 0) {
            startIndex = 0
        }
        var endIndex = startIndex + mShowCandlestickNumber
        if (endIndex > maxEnd) {
            endIndex = maxEnd
        }
        //修正开始的位置
        startIndex = endIndex - mShowCandlestickNumber
        if (startIndex < 0) {
            startIndex = 0
        }
        //如果剩余的数量小于触发加载更多的数量，触发监听
        if (mLoadMoreEnabled && startIndex <= LOAD_MORE_SIZE) {
            mOnKLineViewLoadMoreListener?.loadMore()
        }
        //如果已经滑动到头，停止滑动
        if (startIndex == 0 || endIndex == mKLineData.size - 1) {
            mTouchHelper.forceFinished(true)
        }
        mStartDataIndex = startIndex
        mEndDataIndex = endIndex
        mOnValueSelectedListener?.onLastValueChanged(mKLineData[endIndex])
        calculationMaxAndMin()
        invalidate()
    }

    override fun onSingleTapUp(touchX: Float, touchY: Float) {
        val startY = paddingTop
        val startX = paddingLeft
        val stopX = width - paddingEnd
        var subplotStartY = startY.toFloat()
        var subplotStopY = 0F
        val subplotHeight = mSubplotHeight
        var isFirst = true
        mKLineVolumeList.forEachIndexed { index, kLineIndexInfo ->
            //名称开始的 Y 坐标
            val subplotNameStartY: Float
            //名称结束的 Y 坐标
            val subplotNameStopY: Float
            if (isFirst) {
                subplotNameStartY = subplotStartY
                subplotNameStopY = subplotNameStartY + mKLineViewDimen.mainTopSpace
            } else {
                subplotNameStartY = subplotStopY
                subplotNameStopY = subplotNameStartY + mKLineViewDimen.subplotSpace
            }
            if (touchY > subplotNameStartY && touchY < subplotNameStopY) {
                //线判断是否可以切换
                if (kLineIndexInfo.canSwitch() && kLineIndexInfo.isClickInName(
                        startX = startX.toFloat(),
                        startY = subplotNameStartY,
                        stopX = stopX.toFloat(),
                        stopY = subplotNameStopY,
                        touchX = touchX,
                        touchY = touchY,
                        textPaint = mTextPaint
                    )
                ) {
                    mOnTabNameClickListener?.onTabNameClick(index, kLineIndexInfo.getIndexInfo())
                }
                return
            }
            subplotStartY = subplotNameStopY
            subplotStopY = subplotStartY + kLineIndexInfo.getProportion() * subplotHeight
            if (touchY > subplotStartY && touchY < subplotStopY) {
                if (!mIndexInfoListHelper.canSwitch(kLineIndexInfo)) {
                    return
                }
                mKLineVolumeList[index] = mIndexInfoListHelper.nextIndex(kLineIndexInfo)
                calculationMaxAndMin()
                invalidate()
                return
            }
            if (isFirst) {
                subplotStopY += mKLineViewDimen.dateSpace
            }
            isFirst = false
        }
    }

    override fun onLongPress(touchX: Float, touchY: Float, isFinish: Boolean) {
        if (isFinish) {
            finishLongPress()
            invalidate()
            return
        }
        val candlestickSpace = mCandlestickSpace
        val candlestickWidth = if (mKLineIsLine) {
            candlestickCalculationMinWidth
        } else {
            mCandlestickWidth
        }
        val startIndex = mStartDataIndex
        val endIndex = mEndDataIndex

        //真正触摸在控件内的 X 坐标
        val startX = paddingStart + mKLineViewDimen.borderLineStrokeWidth
        val stopX = width - paddingEnd
        var realTouchX = touchX
        if (touchX < startX) {
            realTouchX = startX
        }
        if (realTouchX > stopX) {
            realTouchX = stopX.toFloat()
        }
        //向下取整，获取当前触摸到屏幕内的位置
        val index = floor((realTouchX - startX) / (candlestickWidth + candlestickSpace)).toInt()
        //开始位置+屏幕中的位置=真正触摸的数据下标
        var touchIndex = startIndex + index
        if (touchIndex > endIndex) {
            touchIndex = endIndex
            //修正触摸的X
            mLongPressX =
                startX + (endIndex - startIndex + 1) * (candlestickWidth + candlestickSpace) - candlestickSpace
        } else {
            mLongPressX = realTouchX
        }
        mCurrentDataIndex = touchIndex
        mOnValueSelectedListener?.onValueSelected(getCurrentData())
        //修正触摸的Y
        val startY = paddingTop + mKLineViewDimen.mainTopSpace
        val stopY = height - paddingBottom
        var realTouchY = touchY
        if (realTouchY < startY) {
            realTouchY = startY
        }
        if (realTouchY > stopY) {
            realTouchY = stopY.toFloat()
        }
        var subplotStartY = startY
        var subplotStopY: Float
        val subplotHeight = mSubplotHeight
        var isFirst = true
        kotlin.run ForEach@{
            mKLineVolumeList.forEachIndexed { index, simpleAvgIndexInfo ->
                val canvasStartY: Float = subplotStartY
                subplotStopY = canvasStartY + simpleAvgIndexInfo.getProportion() * subplotHeight
                if (realTouchY in canvasStartY..subplotStopY) {
                    mLongPressY = realTouchY
                    mLastTouchIndex = index
                    return@ForEach
                }
                val nextStartY = if (isFirst) {
                    subplotStopY + mKLineViewDimen.dateSpace + mKLineViewDimen.subplotSpace
                } else {
                    subplotStopY + mKLineViewDimen.subplotSpace
                }
                if (realTouchY in subplotStopY..nextStartY) {
                    realTouchY = if (mLastTouchIndex == index) {
                        subplotStopY
                    } else {
                        nextStartY
                    }
                    mLongPressY = realTouchY
                    return@ForEach
                }
                subplotStartY = nextStartY
                isFirst = false
            }
        }
        invalidate()
    }

    override fun getCandlestickWidth(): Float {
        return mCandlestickWidth
    }

    /**
     * 绘制日期
     */
    private fun drawDate(
        canvas: Canvas,
        startX: Float,
        subplotStopY: Float,
        stopX: Float,
        stopY: Float
    ) {
        mTextPaint.textSize = mKLineViewDimen.dateTextSize
        mTextPaint.color = mKLineViewColor.dateTextColor
        val textBoundsRect = mRectPool.acquire()
        //开始时间
        val startData = mKLineData[mStartDataIndex]
        val startDate = startData.getKLineDate()
        mTextPaint.getTextBounds(startDate, 0, startDate.length, textBoundsRect)
        val startDateX = startX + mKLineViewDimen.textPaddingLine
        val startDateY =
            subplotStopY + textBoundsRect.height() + mKLineViewDimen.datePaddingTop
        canvas.drawText(startDate, startDateX, startDateY, mTextPaint)
        //中间时间
        val middleData = mKLineData[mStartDataIndex + (mEndDataIndex - mStartDataIndex) / 2]
        val middleDate = middleData.getKLineDate()
        mTextPaint.getTextBounds(middleDate, 0, middleDate.length, textBoundsRect)
        val middleDateX = startX + (stopX - startX - textBoundsRect.width()) / 2
        val middleDateY =
            subplotStopY + textBoundsRect.height() + mKLineViewDimen.datePaddingTop
        canvas.drawText(middleDate, middleDateX, middleDateY, mTextPaint)
        //结束时间
        val endData = mKLineData[mEndDataIndex]
        val endDate = endData.getKLineDate()
        mTextPaint.getTextBounds(endDate, 0, startDate.length, textBoundsRect)
        val endDateY =
            subplotStopY + textBoundsRect.height() + mKLineViewDimen.datePaddingTop
        val endDateX = stopX - textBoundsRect.width() - mKLineViewDimen.textPaddingLine
        canvas.drawText(endDate, endDateX, endDateY, mTextPaint)
        mRectPool.release(textBoundsRect)
    }

    private fun findAvgIndexInfo(): Pair<KLineIndexInfo, Float>? {
        val longPressX = mLongPressX
        val longPressY = mLongPressY
        if (longPressX > 0 && longPressY > 0) {
            val startY = paddingTop + mKLineViewDimen.mainTopSpace
            var subplotStartY = startY
            var indexStopY: Float
            val subplotHeight = mSubplotHeight
            var isFirst = true
            mKLineVolumeList.forEach {
                val indexStartY: Float = subplotStartY
                indexStopY = indexStartY + it.getProportion() * subplotHeight
                if (longPressY in indexStartY..indexStopY) {
                    return Pair(it, 1 - (longPressY - indexStartY) / (indexStopY - indexStartY))
                }
                subplotStartY = if (isFirst) {
                    indexStopY + mKLineViewDimen.dateSpace + mKLineViewDimen.subplotSpace
                } else {
                    indexStopY + mKLineViewDimen.subplotSpace
                }
                isFirst = false
            }
        }
        return null
    }

    private fun getCurrentData(): KLineInfo {
        val currentIndex = if (mCurrentDataIndex < 0) {
            mEndDataIndex
        } else {
            mCurrentDataIndex
        }
        if (currentIndex >= mKLineData.size) {
            return mKLineData.last()
        }
        //绘制指标信息
        return mKLineData[currentIndex]
    }

    /**
     * 结束长按
     */
    private fun finishLongPress() {
        mLongPressX = -1F
        mLongPressY = -1F
        mCurrentDataIndex = -1
        mLastTouchIndex = -1
        mOnValueSelectedListener?.onNothingSelected()
    }

    private fun resetData() {
        mStartDataIndex = 0
        mEndDataIndex = 0
    }
}